package com.hemerasolutions.v2trainer.model.step;

import com.hemerasolutions.v2trainer.model.base.BaseModel;
import com.hemerasolutions.v2trainer.model.exercise.Exercise;
import com.hemerasolutions.v2trainer.model.exercise.TemplateExercise;
import com.hemerasolutions.v2trainer.model.exerciseGroup.ExerciseGroup;
import com.hemerasolutions.v2trainer.model.modality.Modality;
import com.hemerasolutions.v2trainer.model.step.substep.ExerciseGroupStep;
import com.hemerasolutions.v2trainer.model.step.substep.ExerciseGroupedStep;
import com.hemerasolutions.v2trainer.model.step.substep.ExerciseStep;
import com.hemerasolutions.v2trainer.model.step.substep.SubTrainingStep;
import com.hemerasolutions.v2trainer.model.training.Training;
import com.hemerasolutions.v2trainer.model.training.TrainingStep;
import com.hemerasolutions.v2trainer.model.training.statistic.TrainingStatistic;
import com.hemerasolutions.v2trainer.util.Utility;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Transient;

@Entity
public class Step extends BaseModel {
 
	private static final long serialVersionUID = 1L;

    @Column(length = 50)    
	private String name;

    @Column(length = 180)    
	private String description;

    @Column
	private int repetition = 1;
	
    @Column
    private Date restInterval;

	@Column
	private int type;

	@ManyToOne
	private Step parentStep;

    @ManyToOne
    private TrainingStep trainingStep;
	
    @OneToMany(mappedBy="parentStep",cascade=CascadeType.ALL, orphanRemoval=true)
    private List<Step> steps;
    
    @OneToOne(mappedBy="step",cascade=CascadeType.ALL, orphanRemoval=true)
	private TemplateExercise templateExercise;
	
    @OneToOne(mappedBy="step",cascade=CascadeType.ALL, orphanRemoval=true)
    private ExerciseStep exerciseStep;

    @OneToOne(mappedBy="step",cascade=CascadeType.ALL, orphanRemoval=true)
    private ExerciseGroupStep exerciseGroupStep;

    @OneToOne(mappedBy="step",cascade=CascadeType.ALL, orphanRemoval=true)
    private ExerciseGroupedStep exerciseGroupedStep;

    @OneToOne(mappedBy="step",cascade=CascadeType.ALL, orphanRemoval=true)
    private SubTrainingStep subTrainingStep;
    
    @Transient
	private boolean mark = false;

    public static final int ROOT_STEP = -1;
    
	public static final int EXERCISE_STEP = 1;
	
	public static final int EXERCISE_TEMPLATE = 2;

	public static final int EXERCISE_GROUPED = 3;
	
	public static final int ID_STEP = 4;

	public static final int EXERCISE_GROUP = 5;

	public static final int REPETITION_STEP = 6;

	public static final int GROUP_WARMING_UP = 7;
	
	public static final int GROUP_MAIN_SERIES = 8;
	
	public static final int GROUP_SECUNDARY_SERIES = 9;

	public static final int GROUP_TECHNICAL_SERIES = 10;
	
	public static final int GROUP_FINALIZATION = 11;
	
	public static final int GROUP_OTHER = 12;

	public static final int GROUP_TRAINING = 13;

	public static final int GROUP_EXERCISE_GROUP = 14;

	

	public List<Modality> collectModalitys(){
		List<Modality> collecteds = new ArrayList<Modality>();

		if(this.getModality() != null){
			collecteds.add(this.getModality());
		}
		
		for(Step step:this.steps){
			for(Modality obj:step.collectModalitys()){
				if(!collecteds.contains(obj)){
					collecteds.add(obj);
				}
			}
		}
		
		return collecteds;
	}
	
	public List<Training> collectTrainings(){
		List<Training> collecteds = new ArrayList<Training>();

		if(this.getGroupTraining() != null){
			collecteds.add(this.getGroupTraining());
		}

		for(Step step:this.steps){
			for(Training obj:step.collectTrainings()){
				if(!collecteds.contains(obj)){
					collecteds.add(obj);
				}
			}
		}
		return collecteds;
	}
	
	public List<ExerciseGroup> collectExerciseGroups(){
		List<ExerciseGroup> collecteds = new ArrayList<ExerciseGroup>();

		if(this.getExerciseGroup() != null){
			collecteds.add(this.getExerciseGroup());
		}
		
		for(Step step:this.steps){
			for(ExerciseGroup obj:step.collectExerciseGroups()){
				if(!collecteds.contains(obj)){
					collecteds.add(obj);
				}
			}
		}

		return collecteds;
		
	}

	public List<Exercise> collectExercises() {
		List<Exercise> collecteds = new ArrayList<Exercise>();

		if(this.getExercise() != null){
			collecteds.add(this.getExercise());
		}
		
		for(Step step:this.steps){
			for(Exercise obj:step.collectExercises()){
				if(!collecteds.contains(obj)){
					collecteds.add(obj);
				}
			}
		}

		return collecteds;
	}
	
	
	public Modality getModality(){
		
		switch(this.type){
			case EXERCISE_STEP:
				return this.getExerciseStep().getExercise().getModality();
			case EXERCISE_TEMPLATE:
				return this.getTemplateExercise().getModality();
			default:
				return null;
		}
		
	}
	
	public Training getGroupTraining(){
		if(this.type == GROUP_TRAINING ){
			return this.getSubTrainingStep().getSubTraining();
		}
		return null;
	}
	
	public ExerciseGroup getExerciseGroup(){

		if(this.type == EXERCISE_GROUP){
			
		}
		
		ExerciseGroup obj = null;

		return obj;
	}
	
	public Exercise getExercise(){
		Exercise obj = null;
		
		return obj;
		
	}
	
	public Step() {
		super();

		this.id = null;
		this.parentStep = null;
		this.repetition = 1;
		this.uuid = UUID.randomUUID().getMostSignificantBits();
		this.mark = true;
		this.steps = new ArrayList<Step>();
		this.restInterval = Utility.doubleToDate(0);
		
	}
	
	public Step(TrainingStep trainingStep,int stepType,Step parentStep){
		this();
		this.trainingStep = trainingStep;
		this.type = stepType;
		this.parentStep = parentStep;

		switch (this.type) {
		case ROOT_STEP:
			break;
		case EXERCISE_STEP:
			this.exerciseStep = new ExerciseStep(this);
			break;
		case EXERCISE_TEMPLATE:
			this.templateExercise = new TemplateExercise(this);
			break;
		case EXERCISE_GROUPED:
			this.exerciseGroupedStep = new ExerciseGroupedStep(this);
			break;
		case EXERCISE_GROUP:
			this.exerciseGroupStep = new ExerciseGroupStep(this);
			this.mark = false;
			break;
		case ID_STEP:
			break;
		default:
			this.name = this.getStepTypeText();
			this.mark = false;
			break;
		}	
	}
	
	public Step(TrainingStep trainingStep,Step other, Step parent){
		this();
		
		this.type = other.type;
		this.description = other.description;
		this.repetition = other.repetition;
		this.mark = other.mark;
		this.name = other.name;
		this.restInterval = other.getRestInterval();
		this.trainingStep = trainingStep;
		this.parentStep = parent;
		
		switch (this.type) {
			case ROOT_STEP:
				for(Step st:other.getSteps()){
					this.steps.add(new Step(trainingStep,st,this));
				}
				break;
			case EXERCISE_STEP:
				this.exerciseStep = new ExerciseStep(other.exerciseStep,this);
				break;
			case EXERCISE_TEMPLATE:
				this.templateExercise = new TemplateExercise(other.templateExercise,this);
				break;
			case EXERCISE_GROUPED:
				this.exerciseGroupedStep = new ExerciseGroupedStep(other.exerciseGroupedStep,this);
				break;
			case EXERCISE_GROUP:
				this.exerciseGroupStep = new ExerciseGroupStep(other.exerciseGroupStep,this);
				for(Step st:other.getSteps()){
					this.steps.add(new Step(trainingStep,st,this));
				}
				break;
			case ID_STEP:
				break;
			default:
				for(Step st:other.getSteps()){
					this.steps.add(new Step(trainingStep,st,this));
				}
				break;
		}
	}
	
	public TrainingStatistic calcStatistic() {
		switch(this.type){
			case Step.ROOT_STEP:
				return this.calcRootStatistic();
			case Step.EXERCISE_STEP:
				return this.exerciseStep.calcStatistic() ;
			case Step.EXERCISE_TEMPLATE:
				return null;
			case Step.ID_STEP:	
				return this.calcTimeStatistic();
			case Step.EXERCISE_GROUP:
				return this.exerciseGroupStep.calcStatistic();
			case Step.EXERCISE_GROUPED:
				return this.exerciseGroupedStep.calcStatistic();
			default:
				return this.calcGroupStatistic();
		}
		
	}


	
	private TrainingStatistic calcRootStatistic() {

		TrainingStatistic ts = new TrainingStatistic(trainingStep,TrainingStatistic.ROOT_STATISTIC);
		
		ts.clear();

		return ts;
	}

	private TrainingStatistic calcGroupStatistic() {
		
		TrainingStatistic ts = new TrainingStatistic(trainingStep,TrainingStatistic.GROUP_STATISTIC);

		ts.setRestTime(Utility.longToDate(this.calculateRestTime()));
		
		return ts;
	}

	public long calculateRestTime(){
		return Utility.dateToLong(this.getRestInterval());
	}	

	
	private TrainingStatistic calcTimeStatistic() {
		long restTime = Utility.dateToLong(this.restInterval);
		
		TrainingStatistic ts = new TrainingStatistic(trainingStep,TrainingStatistic.REST_STATISTIC);

		ts.setRestTime(Utility.longToDate(restTime * repetition));
		ts.setTotalTime(Utility.longToDate(restTime * repetition));
		
		return ts;
		
	}

	public String getStepTranslated(){
		String timeString = this.getIntervalToString();

		try{
			switch(this.type){
				case Step.ROOT_STEP:
					return this.getSubStepsTranslated();
				case Step.EXERCISE_STEP:
					return this.exerciseStep.getTranslatedExercise() ;
				case Step.EXERCISE_TEMPLATE:
					return this.templateExercise.getTranslatedExercise();
				case Step.ID_STEP:	
					return " ID(" + timeString +")";
				case Step.EXERCISE_GROUP:
					return this.exerciseGroupStep.getTranslatedExercise(getSubStepsTranslated());
				case Step.EXERCISE_GROUPED:
					return this.exerciseGroupedStep.getTranslatedExercise();
				default:
					String stepTranslated = "";

					stepTranslated = this.getName() + " " ;

					stepTranslated += this.repetition > 1 ? 	" " + this.repetition +"x ": " ";
					
					stepTranslated += " (" + this.getSubStepsTranslated()  +" )";

					if(!timeString.equals("") ){
						stepTranslated += " " + "ID: " + timeString;
					}
					
					return stepTranslated;
			}

		}catch(Exception ex){
			 ex.printStackTrace();
		}
		
		return "";
	}

	private String getSubStepsTranslated() {
		String subStepDesc = "";
		
		for(int x = 0; x < this.steps.size();x++){
			Step step = this.steps.get(x);

			subStepDesc += step.getStepTranslated();

			if(x != this.steps.size() - 1){
				subStepDesc += ", " ;
			}
		}				
		
		return subStepDesc;
	}
	
	public String getStepTypeText() {
		switch (this.type) {
		case EXERCISE_STEP:
			return Utility.getText("exercise") ;
		case EXERCISE_TEMPLATE:
			return Utility.getText("exercise") ;
		case EXERCISE_GROUP:
			return this.getName();			
		case ID_STEP:
			return Utility.getText("restInterval");
		case REPETITION_STEP:
			return Utility.getText("Repetitions");
		case GROUP_MAIN_SERIES:
			return Utility.getText("GROUP_MAIN_SERIES");
		case GROUP_SECUNDARY_SERIES:
			return Utility.getText("GROUP_SECUNDARY_SERIES");
		case GROUP_TECHNICAL_SERIES:
			return Utility.getText("GROUP_TECHNICAL_SERIES");
		case GROUP_WARMING_UP:
			return Utility.getText("GROUP_WARMING_UP");
		case GROUP_FINALIZATION:
			return Utility.getText("GROUP_FINALIZATION");
		case GROUP_OTHER:
			return this.getName();
		default:
			return "";	
		}
	}

	public void convertTemplateToExercise(Exercise exercise){
		this.exerciseStep = new ExerciseStep(this.templateExercise,exercise,this);
		
		this.templateExercise = null;
		
		this.type = Step.EXERCISE_STEP;
		
	}
	
	public void convertTemplateToGroup(ExerciseGroup eg){
		this.exerciseGroupStep = new ExerciseGroupStep(this.templateExercise,eg,this);
		this.type = Step.EXERCISE_GROUP;
	}
	
	public void convertToGrouped(){
		if(this.type == EXERCISE_STEP){
			this.exerciseGroupedStep = new ExerciseGroupedStep(this.exerciseStep, this);
			this.exerciseStep = null;
			this.type = EXERCISE_GROUPED;
		}
	}
	
	public void convertToUngrouped(){
		if(this.type == EXERCISE_GROUPED){
			this.exerciseStep = new ExerciseStep(this.exerciseGroupedStep, this);
			this.exerciseGroupedStep = null;
			this.type = EXERCISE_STEP;
		}
		
	}
	
	
	public String getIntervalToString(){
		return Utility.timeToString(this.restInterval);
	}


	public static long getSerialversionuid() {
		return serialVersionUID;
	}

	public String getName() {
		return name;
	}


	public String getDescription() {
		return description;
	}


	public int getRepetition() {
		return repetition;
	}



	public int getType() {
		return type;
	}


	public Date getRestInterval() {
		return restInterval;
	}

	public void setRestInterval(Date intervalDate) {
		this.restInterval = intervalDate;
	}

	public Step getParentStep() {
		return parentStep;
	}

	public void setParentStep(Step parentStep) {
		this.parentStep = parentStep;
	}

	public List<Step> getSteps() {
		return steps;
	}

	public void setSteps(List<Step> steps) {
		this.steps = steps;
	}

	public TemplateExercise getTemplateExercise() {
		return templateExercise;
	}

	public void setTemplateExercise(TemplateExercise templateExercise) {
		this.templateExercise = templateExercise;
	}

	public ExerciseStep getExerciseStep() {
		return exerciseStep;
	}

	public void setExerciseStep(ExerciseStep exerciseStep) {
		this.exerciseStep = exerciseStep;
	}

	public boolean isMark() {
		return mark;
	}

	public void setMark(boolean mark) {
		this.mark = mark;
	}


	public void setName(String name) {
		this.name = name;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public void setRepetition(int repetition) {
		this.repetition = repetition;
	}

	public void setType(int type) {
		this.type = type;
	}
	
	public ExerciseGroupedStep getExerciseGroupedStep() {
		return exerciseGroupedStep;
	}

	public void setExerciseGroupedStep(ExerciseGroupedStep exerciseGroupedStep) {
		this.exerciseGroupedStep = exerciseGroupedStep;
	}

	public ExerciseGroupStep getExerciseGroupStep() {
		return exerciseGroupStep;
	}

	public void setExerciseGroupStep(ExerciseGroupStep exerciseGroupStep) {
		this.exerciseGroupStep = exerciseGroupStep;
	}

	
	public TrainingStep getTrainingStep() {
		return trainingStep;
	}

	public void setTrainingStep(TrainingStep trainingStep) {
		this.trainingStep = trainingStep;
	}


	public SubTrainingStep getSubTrainingStep() {
		return subTrainingStep;
	}

	public void setSubTrainingStep(SubTrainingStep subTrainingStep) {
		this.subTrainingStep = subTrainingStep;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result
				+ ((parentStep == null) ? 0 : parentStep.hashCode());
		result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Step other = (Step) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		if (parentStep == null) {
			if (other.parentStep != null)
				return false;
		} else if (!parentStep.equals(other.parentStep))
			return false;
		if (uuid == null) {
			if (other.uuid != null)
				return false;
		} else if (!uuid.equals(other.uuid))
			return false;
		return true;
	}

}
 
